<template>
  <div ref="editor" class="editor"></div>
</template>
<script>
// import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'; // 引入javascript 语言

import * as monaco from 'monaco-editor'
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
import groovyBeautify from "groovy-beautify"

// 在初始化之前，先设置MonacoEnvironment
self.MonacoEnvironment = {
  getWorker(_, label) {
    if (label === 'json') {
      return new jsonWorker()
    }
    if (label === 'css' || label === 'scss' || label === 'less') {
      return new cssWorker()
    }
    if (label === 'html' || label === 'handlebars' || label === 'razor') {
      return new htmlWorker()
    }
    if (label === 'typescript' || label === 'javascript') {
      return new tsWorker()
    }
    return new editorWorker()
  }
}


export default {
  props: {
    // 根据传过来的数据来设置编辑器语言
    lang: {
      type: String,
      default: ''
    },
    // 根据传过来的数据来设置编辑器的值
    val: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      editor: null,
      editorValue: this.val, // 编辑器的值
      editorShow: false
    }
  },
  watch: {
    lang(val) {
      this.editor.setModelLanguage(this.editor.getModel(), this.lang)
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    init() {
      // 自定义主题背景色
      monaco.editor.defineTheme('CodeSampleTheme', {
        base: 'vs',
        inherit: true,
        rules: [{background: '#F6F6F7'}],
        colors: {
          // 相关颜色属性配置
          'editor.background': '#F6F6F7' // 背景色
        }
      })
      monaco.languages.register({ id: "java" });
      const customTokenProvider = {
        defaultToken: "",
        tokenPostfix: ".java",
        keywords: [
          "abstract",
          "assert",
          "goto",
          "package",
          "synchronized",
          "boolean",
          "private",
          "this",
          "double",
          "implements",
          "protected",
          "throw",
          "byte",
          "import",
          "public",
          "throws",
          "enum",
          "instanceof",
          "transient",
          "extends",
          "int",
          "short",
          "char",
          "final",
          "interface",
          "static",
          "void",
          "class",
          "finally",
          "long",
          "strictfp",
          "volatile",
          "const",
          "float",
          "native",
          "super",
          "true",
          "false",
          "def",
          "null"
        ],
        keywordsControl: [
          "try",
          "catch",
          "return",
          "new",
          "break",
          "case",
          "else",
          "if",
          "switch",
          "for",
          "while",
          "do",
          "default",
          "continue"
        ],
        operators: [
          "=",
          ">",
          "<",
          "!",
          "~",
          "?",
          ":",
          "==",
          "<=",
          ">=",
          "!=",
          "&&",
          "||",
          "++",
          "--",
          "+",
          "-",
          "*",
          "/",
          "&",
          "|",
          "^",
          "%",
          "<<",
          ">>",
          ">>>",
          "+=",
          "-=",
          "*=",
          "/=",
          "&=",
          "|=",
          "^=",
          "%=",
          "<<=",
          ">>=",
          ">>>="
        ],
        // typeKeywords: [
        //   "aaa",
        // ],
        // we include these common regular expressions
        symbols: /[=><!~?:&|+\-*\/\^%]+/,
        escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
        digits: /\d+(_+\d+)*/,
        octaldigits: /[0-7]+(_+[0-7]+)*/,
        binarydigits: /[0-1]+(_+[0-1]+)*/,
        hexdigits: /[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,
        // keywordsss: ["error", "info", "warning"],
        // The main tokenizer for our languages
        tokenizer: {
          root: [
            // Special keyword with a dash
            [
              /[A-Z][a-z|0-9|\u4e00-\u9fa5|@|_|-]*/,
              {
                token: "custom-variable"
              }
            ],
            [
              /[a-zA-Z_$][\w$]*/,
              {
                cases: {
                  "@keywords": { token: "keyword.$0" },
                  "@keywordsControl": { token: "custom-keywords" },
                  "@default": "identifier"
                }
              }
            ],
            { include: "@whitespace" },

            // delimiters and operators
            [/[{}()\[\]]/, "@brackets"],
            [/[<>](?!@symbols)/, "@brackets"],
            [
              /@symbols/,
              {
                cases: {
                  "@operators": "delimiter",
                  "@default": ""
                }
              }
            ],

            // @ annotations.
            [/@\s*[a-zA-Z_\$][\w\$]*/, "annotation"],

            // numbers
            [/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/, "number.float"],
            [/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/, "number.float"],
            [/0[xX](@hexdigits)[Ll]?/, "number.hex"],
            [/0(@octaldigits)[Ll]?/, "number.octal"],
            [/0[bB](@binarydigits)[Ll]?/, "number.binary"],
            [/(@digits)[fFdD]/, "number.float"],
            [/(@digits)[lL]?/, "number"],

            // delimiter: after number because of .\d floats
            [/[;,.]/, "delimiter"],

            // strings
            [/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string
            [/"/, "string", "@string"],

            // characters
            [/'[^\\']'/, "string"],
            [/(')(@escapes)(')/, ["string", "string.escape", "string"]],
            [/'/, "string.invalid"]
          ],

          // text: [
          //   [/^@header/, { token: "@rematch", next: "@pop" }],
          //   [/.*/, { token: "custom-$S2" }]
          // ],
          whitespace: [
            [/[ \t\r\n]+/, ""],
            [/\/\*\*(?!\/)/, "comment.doc", "@javadoc"],
            [/\/\*/, "comment", "@comment"],
            // [/\/\/.*$/, "custom-note"]
            [/\/\/.*$/, "comment"]
          ],

          comment: [
            [/[^\/*]+/, "comment"],
            // [/\/\*/, 'comment', '@push' ],    // nested comment not allowed :-(
            // [/\/\*/,    'comment.invalid' ],    // this breaks block comments in the shape of /* //*/
            [/\*\//, "comment", "@pop"],
            [/[\/*]/, "comment"]
          ],
          //Identical copy of comment above, except for the addition of .doc
          javadoc: [
            [/[^\/*]+/, "comment.doc"],
            // [/\/\*/, 'comment.doc', '@push' ],    // nested comment not allowed :-(
            [/\/\*/, "comment.doc.invalid"],
            [/\*\//, "comment.doc", "@pop"],
            [/[\/*]/, "comment.doc"]
          ],

          string: [
            [/[^\\"]+/, "string"],
            [/@escapes/, "string.escape"],
            [/\\./, "string.escape.invalid"],
            [/"/, "string", "@pop"]
          ]
          // javaVariabel:[
          //   [/def+/, "variabel"],
          // ]
        }
      }
      const customLanguageConfiguration = monaco => {
        return {
          wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
          comments: {
            lineComment: "//",
            blockComment: ["/*", "*/"]
          },
          brackets: [
            ["{", "}"],
            ["[", "]"],
            ["(", ")"]
          ],
          onEnterRules: [
            {
              // e.g. /** | */
              beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
              afterText: /^\s*\*\/$/,
              action: {
                indentAction: monaco.languages.IndentAction.IndentOutdent,
                appendText: " * "
              }
            },
            {
              // e.g. /** ...|
              beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
              action: {
                indentAction: monaco.languages.IndentAction.None,
                appendText: " * "
              }
            },
            {
              // e.g.  * ...|
              beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
              action: {
                indentAction: monaco.languages.IndentAction.None,
                appendText: "* "
              }
            },
            {
              // e.g.  */|
              beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
              action: {
                indentAction: monaco.languages.IndentAction.None,
                removeText: 1
              }
            }
          ],
          autoClosingPairs: [
            { open: "{", close: "}" },
            { open: "[", close: "]" },
            { open: "(", close: ")" },
            { open: '"', close: '"' },
            { open: "'", close: "'" },
            { open: "<", close: ">" },
            { open: "`", close: "`", notIn: ["string", "comment"] },
            { open: "/**", close: " */", notIn: ["string"] }
          ],
          surroundingPairs: [
            { open: "{", close: "}" },
            { open: "[", close: "]" },
            { open: "(", close: ")" },
            { open: '"', close: '"' },
            { open: "'", close: "'" },
            { open: "<", close: ">" }
          ],
          folding: {
            markers: {
              start: new RegExp("^\\s*//\\s*(?:(?:#?region\\b)|(?:<editor-fold\\b))"),
              end: new RegExp("^\\s*//\\s*(?:(?:#?endregion\\b)|(?:</editor-fold>))")
            }
          }
        };
      }

      monaco.languages.setMonarchTokensProvider("java",customTokenProvider);
      monaco.languages.setLanguageConfiguration(
          "java",
          customLanguageConfiguration(monaco)
      );


      // 初始化monaco编辑器
      this.editor = monaco.editor.create(this.$refs.editor, {
        value: this.editorValue, // 编辑器初始显示文字
        theme: 'vs-dark', // 官方自带三种主题vs, hc-black, or vs-dark
        language: this.lang, // 语言支持自行查阅demo
        overviewRulerBorder: true, // 滚动是否有边框
        automaticLayout: true, // 自动布局
        readOnly: false, // 是否只读
        minimap: { // 关闭代码缩略图
          enabled: false // 是否启用预览图
        },
        lineNumbers: 'on', // 控制行号的显隐
        scrollBeyondLastLine: false, // 禁用额外滚动区
        scrollbar: {
          verticalScrollbarSize: 4, // 垂直滚动条宽度，默认px
          horizontalScrollbarSize: 4 // 水平滚动条高度
        },
        contextmenu: true // 禁用右键菜单
      })
      // 监听内容变化
      this.editor.onDidChangeModelContent(() => {
      })
      // 监听失去焦点事件
      this.editor.onDidBlurEditorText((e) => {
      })

      //java代码格式化问题，这里使用了groovyBeautify 他是npm里第三方格式化java代码的插件库
      monaco.languages.registerDocumentFormattingEditProvider("java", {
        provideDocumentFormattingEdits: function(model, options, token) {
          const formattedCode = groovyBeautify(model.getValue());
          return [
            {
              range: model.getFullModelRange(),
              text: formattedCode
            }
          ];
        }
      });
      //java代码提示问题
      //这里使用了registerCompletionItemProvider
      this.languages = monaco.languages.registerCompletionItemProvider(
          "java",
          {
            provideCompletionItems: (model, position, context, token) => {
              console.log(model, position, context, token)
              const textUntilPosition = model.getValueInRange({
                startLineNumber: position.lineNumber,
                startColumn: 1,
                endLineNumber: position.lineNumber,
                endColumn: position.column
              });
              let match = textUntilPosition.match(/(\S+)$/);
              if (!match) return [];
              match = match[0];
              let suggestions = [];
              customTokenProvider.keywords.forEach(it => {
                if (it.indexOf(match) !== -1) {
                  suggestions.push({
                    label: it,
                    kind: monaco.languages.CompletionItemKind.Keyword,
                    insertText: it
                  });
                }
              })
              customTokenProvider.operators.forEach(it => {
                if (it.indexOf(match) !== -1) {
                  suggestions.push({
                    label: it,
                    kind: monaco.languages.CompletionItemKind.Operator,
                    insertText: it
                  });
                }
              })
              customTokenProvider.keywordsControl.forEach(it => {
                if (it.indexOf(match) !== -1) {
                  suggestions.push({
                    label: it,
                    kind: monaco.languages.CompletionItemKind.Keyword,
                    insertText: it
                  });
                }
              })
              return {
                suggestions: suggestions
              };
            }
          }
      )
    },
    // 设置并获取monaco编辑器的值
    setEditorValue(data) {
      const newModel = monaco.editor.createModel(
          data, // 值
          this.lang // 语言
      )
      this.editor.setModel(newModel)
      // 设置自定义的主题背景色
      monaco.editor.setTheme('CodeSampleTheme')
      // 获取monaco编辑器的值
      this.editorValue = this.editor.getValue()
      if (this.editorValue) {
        this.editorShow = true
      }
    }
  }
}
</script>
<style scoped lang="scss">
.editor {
  width: 100%;
  min-height: 250px;
}
</style>